package de.godlike.services.internal;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.LineNumberReader;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.log4j.Logger;
import org.apache.log4j.lf5.LogLevel;

import de.godlike.services.internal.dataTypes.LogEntry;

/**
 * This class reads the logFile of JWebminService and returns it as a list of
 * {@link LogEntry}-elements. The list of {@link LogEntry}-elements can be
 * filtered by giving a logLevel.
 * 
 * @author asmodaeus
 * 
 */
public class LogReader {
	private final Logger logger = Logger.getLogger(LogReader.class);
	/**
	 * logLevel 2 filter for
	 */
	private String logLevelFilter = null;
	/**
	 * the content of "catalina.home"-system-Property
	 */
	private final String catalinaHome = System.getProperty("catalina.home");
	/**
	 * logFile 2 read
	 */
	private File log = null;
	/**
	 * yes, the logFileReader, maybe you got it already by reading the name
	 */
	private LineNumberReader logFileReader = null;
	private final DateFormat logDateFormat = new SimpleDateFormat(
			"yyyy-MM-dd HH:mm:ss,SSS");
	private final Pattern logPattern = Pattern.compile(
			"^(.* .*) (.*) (.*) - (.*)$", Pattern.CASE_INSENSITIVE);

	/**
	 * constructs a new LogReader-Object and tries to access the
	 * JWebminService-LogFile.
	 */
	public LogReader() {
		if (this.catalinaHome != null) {
			this.log = new java.io.File(this.catalinaHome
					+ "/logs/JWebminService.log");
			try {
				this.logFileReader = new LineNumberReader(new FileReader(
						this.log));

			} catch (FileNotFoundException e) {
				this.logger
						.warn("There was no logFile to read from... well now there should be one :-)",
								e);
			}
		}
	}

	/**
	 * calls {@link #LogReader()} and sets a logLevel 2 filter for.
	 * 
	 * @param logLevel
	 *            2 filter for
	 */
	public LogReader(String logLevel) {
		this();
		if (!this.isValidLogLevel(logLevel)) {
			throw new IllegalArgumentException(
					"Only valid loglevels are allowed. See org.apache.log4j.lf5.LogLevel for more information");
		}
		this.setLogLevel(logLevel);
	}

	/**
	 * checks, whether the given logLevel is a valid log4j-LogLevel (
	 * {@link org.apache.log4j.lf5.LogLevel}).
	 * 
	 * @param level
	 *            2 check.
	 * @return true, if it's valid, false if not
	 */
	@SuppressWarnings("unchecked")
	private boolean isValidLogLevel(String level) {
		for (String levelToTest : (List<String>) LogLevel.getAllDefaultLevels()) {
			if (levelToTest.equalsIgnoreCase(level)) {
				return true;
			}
		}
		return false;
	}

	/**
	 * @param logLevel
	 *            the logLevel to set
	 */
	public void setLogLevel(String logLevel) {
		this.logLevelFilter = logLevel;
	}

	/**
	 * @return the logLevel
	 */
	public String getLogLevel() {
		return this.logLevelFilter;
	}

	/**
	 * checks, whether the given LogEntry matches to all used filters.
	 * 
	 * @param entry2Filter
	 *            entry, that shall be matched to all filters
	 * @return true, if entry matches to all filters, false otherwise
	 */
	private boolean filterEntry(LogEntry entry2Filter) {
		// Filter: logLevel
		if (this.logLevelFilter != null) {
			if (!entry2Filter.getLogLevel().equalsIgnoreCase(
					this.logLevelFilter)) {
				return false;
			}
		}
		// TODO: more Filters :-)
		return true;
	}

	// 2011-03-16 20:45:25,777 DEBUG
	// de.godlike.services.JWebminService.getDiskList(JWebminService.java:157) -
	// call to JWebminService.getDiskList()

	/**
	 * @return a list with all LogEntry-line that match to the given filters.
	 */
	public List<LogEntry> getLogEntries() {

		List<LogEntry> returnList = new ArrayList<LogEntry>();

		try {
			// read logFile
			while (this.logFileReader.ready()) {

				String line = this.logFileReader.readLine();
				LogEntry tmpEntry = new LogEntry();
				Matcher logMatcher = this.logPattern.matcher(line);

				// match regex to current logLine
				if (logMatcher.matches()) {
					try {
						// 1. logDate
						try {
							tmpEntry.setLogDate(this.logDateFormat
									.parse(logMatcher.group(1)));
						} catch (ParseException e) {
							this.logger.warn("logDate \"" + logMatcher.group(1)
									+ "\" could not be parsed as Date!", e);
						}
						// 2. logLevel
						tmpEntry.setLogLevel(logMatcher.group(2));
						// 3. logClass
						tmpEntry.setLogClass(logMatcher.group(3));
						// 4. logMessage
						tmpEntry.setLogMessage(logMatcher.group(4));
					} catch (IndexOutOfBoundsException e) {
						this.logger.warn("logEntry \"" + line
								+ "\" could not be parsed as LogEntry!", e);
					}
				}

				// only add entry, if it matches to filters
				if (this.filterEntry(tmpEntry)) {
					returnList.add(tmpEntry);
				}
			}
		} catch (IOException e) {
			this.logger.error(
					"Something went wrong reading the JWebminService logFile!",
					e);
		}

		return returnList;
	}
}
